#include "stdafx.h"
#include "tests.core.dual.h"
#include "tests.core.dual.impl.h"
#include "tests.core.dual.handle_ptr.h"


namespace tests { 

    using namespace Microsoft::VisualStudio::CppUnitTestFramework;

    template<class T> struct alias {
        typedef T type;
    };

    struct VEX_ABSTRACT a1 : public vex::define_contract<a1> {
        virtual void call_a1() = 0;
        virtual bool save(vex::handle<a1>**);
        virtual vex::handle<a1>* clone(alias<a1> const &) = 0;
    };

    struct VEX_ABSTRACT a2 : public vex::define_contract<a2> {
        virtual void call_a2() = 0;
        virtual bool save(vex::handle<a2>**);
        virtual vex::handle<a2>* clone(alias<a2> const &) = 0;
    };

    struct VEX_ABSTRACT a3 : public vex::define_contract<a3> {
        virtual void call_a3() = 0;
        virtual bool save(vex::handle<a3>**);
        virtual vex::handle<a3>* clone(alias<a3> const &) = 0;
    };

    struct VEX_ABSTRACT b1 : public vex::define_contract< b1, vex::extends<a1, a2> > {
        virtual void call_b1() = 0;
        virtual bool save(vex::handle<b1>**);
        virtual vex::handle<b1>* clone(alias<b1> const &) = 0;
    };


    struct VEX_ABSTRACT c1 : public vex::define_contract< c1, vex::extends<b1, a3> > {
        virtual void call_c1() = 0;
        virtual bool save(vex::handle<c1>**);
        virtual vex::handle<c1>* clone(alias<c1> const &) = 0;
    };

    namespace core_dual_auto_impl {

        struct in_tag;
        struct out_tag;
        struct in_out_tag;

        enum class argument_kind {
            in,
            out,
            in_out
        };

        class VEX_ABSTRACT a1_impl 
            : public vex::define_implementation< a1_impl, vex::implements<a1> >
        {
            VEX_NON_COPYABLE(a1_impl);
        protected:
            a1_impl() {}
            ~a1_impl() {}
        public:
            virtual void call_a1() {}
            virtual bool save(vex::handle<a1>** h)
            {
                if (h) {
                    *h = vex::make_handle<a1_impl>();
                    return *h ? true : false;
                }
                return false;
            }
            virtual vex::handle<a1>* clone(alias<a1> const &)
            {
                return vex::make_handle<a1_impl>();
            }
        };

        class VEX_ABSTRACT b1_impl 
            : public vex::define_implementation< b1_impl, vex::implements<b1> >
        {
            VEX_NON_COPYABLE(b1_impl);
        protected:
            b1_impl() {}
            ~b1_impl() {}
        public:
            virtual void call_a1() {}
            virtual void call_a2() {}
            virtual void call_b1() {}
        };

        class VEX_ABSTRACT c1_impl
            : public vex::define_implementation< c1_impl, vex::implements<c1> >
        {
            VEX_NON_COPYABLE(c1_impl);
        protected:
            c1_impl() {}
            ~c1_impl() {}
        public:
        };
        
        TEST_CLASS(test_class)
        {
            VEX_TESTS_TRACKED_MEM_CHECK();
            BEGIN_TEST_CLASS_ATTRIBUTE()
                TEST_CLASS_ATTRIBUTE(L"Module", L"tests.core.dual.auto_impl")
            END_TEST_CLASS_ATTRIBUTE();
        public:
            TEST_METHOD_INITIALIZE(set_up)
            {
                VEX_TESTS_TRACKED_MEM_CHECK_START();
            }

            TEST_METHOD_CLEANUP(tear_down)
            {
                VEX_TESTS_TRACKED_MEM_CHECK_FINISH();
            }

            TEST_METHOD(type_relations_are_as_expected)
            {
                Assert::IsTrue(std::is_base_of<a1, b1>::value);
                Assert::IsTrue(std::is_base_of<a2, b1>::value);
                Assert::IsTrue(std::is_base_of<b1, c1>::value);
                Assert::IsTrue(std::is_base_of<a3, c1>::value);
            }

            TEST_METHOD(default_extends_has_size_zero)
            {
                Assert::AreEqual(0, (int)vex::extends<>::size, wlog_message() 
                << typeid(vex::extends<>).name() << std::endl | as_string);
            }

            TEST_METHOD(default_extends_has_nothing_as_head_and_tail)
            {
                Assert::IsTrue(std::is_same<
                    vex::core::detail::nothing, 
                    vex::extends<>::head_type
                >::value, wlog_message() 
                << typeid(vex::extends<>::head_type).name() << std::endl | as_string);

                Assert::IsTrue(std::is_same<
                    vex::core::detail::nothing, 
                    vex::extends<>::tail_type
                >::value, wlog_message() 
                << typeid(a1::base_list_type::tail_type).name() << std::endl | as_string);
            }

            TEST_METHOD(a1_internal_type_relations_are_as_expected)
            {
                Assert::IsTrue(std::is_base_of<vex::define_contract<a1, vex::extends<>>, a1>::value);
                Assert::IsFalse(std::is_base_of<vex::extends<>, a1>::value);
                Assert::IsTrue(std::is_same<a1, a1::contract_type>::value);
                Assert::IsTrue(std::is_same<vex::extends<>, a1::base_list_type>::value);
                
                Assert::IsTrue(std::is_same<
                    vex::core::detail::nothing, 
                    a1::base_list_type::head_type
                >::value, wlog_message() 
                << typeid(a1::base_list_type::head_type).name() << std::endl | as_string);

                Assert::IsTrue(std::is_same<
                    vex::core::detail::nothing, 
                    a1::base_list_type::tail_type
                >::value, wlog_message() 
                << typeid(a1::base_list_type::tail_type).name() << std::endl | as_string);

                Assert::AreEqual(std::size_t(0u), (std::size_t)a1::base_list_type::size);
            }

            TEST_METHOD(for_a1_a2_a3_query_handle_can_be_called_unambigously)
            {
                {
                    typedef decltype(std::declval<a1*>()
                        ->query_handle(std::declval<vex::handle<a1>**>())) result_t;
                    Assert::IsTrue(std::is_same<bool, result_t>::value);
                }

                {
                    typedef decltype(std::declval<a2*>()
                        ->query_handle(std::declval<vex::handle<a2>**>())) result_t;
                    Assert::IsTrue(std::is_same<bool, result_t>::value);
                }

                {
                    typedef decltype(std::declval<a3*>()
                        ->query_handle(std::declval<vex::handle<a3>**>())) result_t;
                    Assert::IsTrue(std::is_same<bool, result_t>::value);
                }
            }

            TEST_METHOD(b1_internal_type_relations_are_as_expected)
            {
                using namespace vex;
                Assert::IsTrue(std::is_base_of<
                    define_contract< b1, extends<a1, a2> >, 
                    b1
                >::value);

                Assert::IsTrue(std::is_base_of<
                    extends<a1, a2>, 
                    b1 
                >::value);

                Assert::IsTrue(std::is_same<b1, b1::contract_type>::value);

                Assert::IsTrue(std::is_same<extends<a1, a2>, b1::base_list_type>::value);
                Assert::IsTrue(std::is_same<a1, b1::base_list_type::head_type>::value);
                Assert::IsTrue(std::is_same<vex::extends<a2>, b1::base_list_type::tail_type>::value);
                Assert::IsTrue(std::is_same<a2, b1::base_list_type::tail_type::head_type>::value);
                Assert::IsTrue(std::is_same<a2, b1::base_list_type::tail_type::head_type::contract_type>::value);
                Assert::IsTrue(std::is_same<
                    vex::extends<>, 
                    b1::base_list_type::tail_type::head_type::base_list_type
                >::value);
                Assert::AreEqual((std::size_t)2u, (std::size_t)b1::size);
            }

            TEST_METHOD(for_b1_query_handle_with_all_base_contracts_can_be_called_unambigously)
            {
                {
                    typedef decltype(std::declval<b1*>()
                        ->query_handle(std::declval<vex::handle<b1>**>())) result_t;
                    Assert::IsTrue(std::is_same<bool, result_t>::value);
                }

                {
                    typedef decltype(std::declval<b1*>()
                        ->query_handle(std::declval<vex::handle<a1>**>())) result_t;
                    Assert::IsTrue(std::is_same<bool, result_t>::value);
                }

                {
                    typedef decltype(std::declval<b1*>()
                        ->query_handle(std::declval<vex::handle<a2>**>())) result_t;
                    Assert::IsTrue(std::is_same<bool, result_t>::value);
                }
            }

            TEST_METHOD(c1_internal_type_relations_are_as_expected)
            {
                Assert::IsTrue(std::is_same<c1::contract_type, c1>::value);
                Assert::IsTrue(std::is_same<c1::base_list_type, vex::extends<b1, a3>>::value);
                // is b1 head of c1::base_list_type?
                Assert::IsTrue(std::is_same<b1, c1::base_list_type::head_type>::value);
                // is extends<a3> tail of c1::base_list_type?
                Assert::IsTrue(std::is_same<vex::extends<a3>, c1::base_list_type::tail_type>::value);

                // b1 part of c1:
                Assert::IsTrue(std::is_same<b1, 
                    c1::base_list_type::head_type::contract_type>::value);
                Assert::IsTrue(std::is_same<vex::extends<a1, a2>, 
                    c1::base_list_type::head_type::base_list_type>::value);
                // a1 part
                Assert::IsTrue(std::is_same<a1, 
                    c1::base_list_type::head_type::base_list_type::head_type>::value);
                Assert::IsTrue(std::is_same<vex::extends<>,
                    c1::base_list_type::head_type::base_list_type::head_type::base_list_type>::value);
                // a2 part
                Assert::IsTrue(std::is_same<vex::extends<a2>, 
                    c1::base_list_type::head_type::base_list_type::tail_type>::value);
            }

            TEST_METHOD(for_c1_query_handle_with_all_base_contracts_can_be_called_unambigously)
            {
                {
                    typedef decltype(std::declval<c1*>()
                        ->query_handle(std::declval<vex::handle<c1>**>())) result_t;
                    Assert::IsTrue(std::is_same<bool, result_t>::value);
                }

                {
                    typedef decltype(std::declval<c1*>()
                        ->query_handle(std::declval<vex::handle<b1>**>())) result_t;
                    Assert::IsTrue(std::is_same<bool, result_t>::value);
                }

                {
                    typedef decltype(std::declval<c1*>()
                        ->query_handle(std::declval<vex::handle<a1>**>())) result_t;
                    Assert::IsTrue(std::is_same<bool, result_t>::value);
                }

                {
                    typedef decltype(std::declval<c1*>()
                        ->query_handle(std::declval<vex::handle<a2>**>())) result_t;
                    Assert::IsTrue(std::is_same<bool, result_t>::value);
                }

                {
                    typedef decltype(std::declval<c1*>()
                        ->query_handle(std::declval<vex::handle<a3>**>())) result_t;
                    Assert::IsTrue(std::is_same<bool, result_t>::value);
                }
            }

            TEST_METHOD(c1_save_as_replacement_for_covariant_return_types_can_be_called_unambiguosly)
            {
                {
                    typedef decltype(std::declval<c1*>()
                        ->save(std::declval<vex::handle<c1>**>())) result_t;
                    Assert::IsTrue(std::is_same<bool, result_t>::value);
                }

                {
                    typedef decltype(std::declval<b1*>()
                        ->save(std::declval<vex::handle<b1>**>())) result_t;
                    Assert::IsTrue(std::is_same<bool, result_t>::value);
                }

                {
                    typedef decltype(std::declval<a1*>()
                        ->save(std::declval<vex::handle<a1>**>())) result_t;
                    Assert::IsTrue(std::is_same<bool, result_t>::value);
                }

                {
                    typedef decltype(std::declval<a2*>()
                        ->save(std::declval<vex::handle<a2>**>())) result_t;
                    Assert::IsTrue(std::is_same<bool, result_t>::value);
                }

                {
                    typedef decltype(std::declval<a3*>()
                        ->save(std::declval<vex::handle<a3>**>())) result_t;
                    Assert::IsTrue(std::is_same<bool, result_t>::value);
                }
            }

            TEST_METHOD(c1_clone_with_alias_as_replacement_for_covariant_return_types_can_be_called_unambiguosly)
            {
                {
                    typedef decltype(std::declval<c1*>()
                        ->clone(alias<c1>())) result_t;
                    Assert::IsTrue(std::is_same<vex::handle<c1>*, result_t>::value);
                }

                {
                    typedef decltype(std::declval<b1*>()
                        ->clone(alias<b1>())) result_t;
                    Assert::IsTrue(std::is_same<vex::handle<b1>*, result_t>::value);
                }

                {
                    typedef decltype(std::declval<a1*>()
                        ->clone(alias<a1>())) result_t;
                    Assert::IsTrue(std::is_same<vex::handle<a1>*, result_t>::value);
                }

                {
                    typedef decltype(std::declval<a2*>()
                        ->clone(alias<a2>())) result_t;
                    Assert::IsTrue(std::is_same<vex::handle<a2>*, result_t>::value);
                }

                {
                    typedef decltype(std::declval<a3*>()
                        ->clone(alias<a3>())) result_t;
                    Assert::IsTrue(std::is_same<vex::handle<a3>*, result_t>::value);
                }
            }

            TEST_METHOD(a1_impl_internal_types)
            {
                Assert::IsTrue(std::is_same<a1_impl, a1_impl::impl_type>::value);
                Assert::IsTrue(std::is_same<vex::implements<a1>, a1_impl::base_list_type>::value);
                Assert::IsTrue(std::is_base_of<a1, a1_impl>::value);
                Assert::IsTrue(std::is_base_of<vex::handle<a1>, a1_impl>::value);
                Assert::IsTrue(std::is_base_of<a1_impl, vex::core::handle_from_base<a1_impl> >::value);
            }

            TEST_METHOD(a1_impl_can_be_instantiated_and_methods_can_be_called_unambigously)
            {
                Assert::IsTrue(std::is_convertible<
                    decltype(vex::make_handle<a1_impl>()), vex::handle<a1>*
                >::value);

                Assert::IsFalse(std::is_abstract< vex::core::handle_from_base<a1_impl> >::value);

                vex::handle_ptr<a1> hap = vex::make_handle<a1_impl>();
                Assert::IsTrue(hap);

                hap->call_a1();

                vex::handle<a1>* ha;
                Assert::IsTrue(hap->query_handle(&ha));
                vex::handle_ptr<a1> h_another = ha;
                a1* pa;
                Assert::IsTrue(ha->query_interface(&pa));
                Assert::IsTrue(hap.get() == pa);

                // clone
                vex::handle_ptr<a1> t_result_of_clone = hap->clone(alias<a1>());
                Assert::IsTrue(t_result_of_clone);

                // save
                vex::handle<a1>* t_handle_for_save;
                Assert::IsTrue(hap->save(&t_handle_for_save));
                vex::handle_ptr<a1> t_result_of_save = t_handle_for_save;
                Assert::IsTrue(t_result_of_save);
            }

            //TEST_METHOD(b1_impl_internal_types)
            //{
            //    Assert::IsTrue(std::is_same<b1_impl, b1_impl::impl_type>::value);
            //    Assert::IsTrue(std::is_same<vex::implements<b1>, b1_impl::base_list_type>::value);
            //    Assert::IsTrue(std::is_base_of<b1, b1_impl>::value);
            //    Assert::IsTrue(std::is_base_of<vex::handle<b1>, b1_impl>::value);
            //    Assert::IsTrue(std::is_base_of<b1_impl, vex::core::handle_from_base<b1_impl> >::value);

            //    // base types:
            //    Assert::IsTrue(std::is_base_of<a1, b1_impl>::value);
            //    Assert::IsTrue(std::is_base_of<a2, b1_impl>::value);
            //}

            //TEST_METHOD(b1_impl_can_be_instantiated_and_methods_can_be_called_unambigously)
            //{
            //    Assert::IsTrue(std::is_convertible<
            //        decltype(vex::make_handle<b1_impl>()), vex::handle<b1>*
            //    >::value);

            //    Assert::IsFalse(std::is_abstract< vex::core::handle_from_base<b1_impl> >::value);

            //    vex::handle_ptr<b1> h = vex::make_handle<b1_impl>();
            //    Assert::IsTrue(h);

            //    h->call_a1();
            //    h->call_a2();
            //    h->call_b1();

            //    vex::handle<b1>* hb;
            //    Assert::IsTrue(h->query_handle(&hb));
            //    b1* pb;
            //    Assert::IsTrue(hb->query_interface(&pb));
            //    Assert::IsTrue(h.get() == pb);
            //}
        };
    }
}